//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Copyright notice: Parts of this file taken (with permission) from the NewOS source

#ifndef ELFEXEC_H
#define ELFEXEC_H

// Includes
#include <multiboot.h>

// Types

// elf format integer types
typedef uint32_t Elf32_Addr;
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Off;
typedef int32_t Elf32_Sword;
typedef uint32_t Elf32_Word;

// the ELF file header
struct Elf32_Ehdr {
    uint8_t     e_ident[16]; // identification bytes
    Elf32_Half  e_type; // elf object type
    Elf32_Half  e_machine; // target machine type
    Elf32_Word  e_version; // file version
    Elf32_Addr  e_entry; // program entry point
    Elf32_Off   e_phoff; // first program header offset
    Elf32_Off   e_shoff; // first section header offset
    Elf32_Word  e_flags; // object file flags
    Elf32_Half  e_ehsize; // size of the elf header
    Elf32_Half  e_phentsize; // program header entry size
    Elf32_Half  e_phnum; // number of program header entries
    Elf32_Half  e_shentsize; // size of section header entries
    Elf32_Half  e_shnum; // number of section header entries
    Elf32_Half  e_shstrndx; // index in section header of section name string table
} __attribute__((packed));

// a section header
struct Elf32_Shdr {
    Elf32_Word  sh_name; // index into string table for the name
    Elf32_Word  sh_type; // header type
    Elf32_Word  sh_flags; // header flags
    Elf32_Addr  sh_addr; // header address
    Elf32_Off   sh_offset; // header offset (in file)
    Elf32_Word  sh_size; // header size
    Elf32_Word  sh_link; // a link to another relevant section
    Elf32_Word  sh_info; // header info
    Elf32_Word  sh_addralign; // alignment for addreses
    Elf32_Word  sh_entsize; // size of entries in this section (ie, symbol table)
} __attribute__((packed));

// a symbol in a symbol table
struct Elf32_Sym {
    Elf32_Word  st_name;
    Elf32_Addr  st_value;
    Elf32_Word  st_size;
    uint8_t     st_info;
    uint8_t     st_other;
    Elf32_Half  st_shndx;
} __attribute__((packed));

// relocation entry without an addend
struct Elf32_Rel {
    Elf32_Addr  r_offset;
    Elf32_Word  r_info;
} __attribute__((packed));

// relocation entry with an addend
struct Elf32_Rela {
    Elf32_Addr  r_offset;
    Elf32_Word  r_info;
    Elf32_Sword r_addend;
} __attribute__((packed));

// a program header
struct Elf32_Phdr {
    Elf32_Word  p_type;
    Elf32_Off   p_offset;
    Elf32_Addr  p_vaddr;
    Elf32_Addr  p_paddr;
    Elf32_Word  p_filesz; // size of this section in the file
    Elf32_Word  p_memsz; // size of this section in memory
    Elf32_Word  p_flags;
    Elf32_Word  p_align;
} __attribute__((packed));

// dynamic section data
struct Elf32_Dyn {
    Elf32_Sword d_tag;
    union {
        Elf32_Word d_val;
        Elf32_Addr d_ptr;
    } d_un;
} __attribute__((packed));

// Defines

// file types
#define ET_NONE		0
#define ET_REL		1
#define ET_EXEC		2
#define ET_DYN		3
#define ET_CORE		4
#define ET_LOPROC	0xff00
#define ET_HIPROC	0xffff

// machine types (ie, architectures)
#define EM_NONE     0
#define EM_M32      1
#define EM_SPARC    2
#define EM_386      3
#define EM_68K      4
#define EM_88K      5
#define EM_860      7
#define EM_MIPS     8

// version entries
#define EV_NONE     0
#define EV_CURRENT  1

// offsets into e_ident
#define EI_MAG0     0
#define EI_MAG1     1
#define EI_MAG2     2
#define EI_MAG3     3
#define EI_CLASS    4
#define EI_DATA     5
#define EI_VERSION  6
#define EI_PAD      7
#define EI_NIDENT   16 // number of bytes in the e_ident array

// program classes
#define ELFCLASSNONE    0
#define ELFCLASS32      1
#define ELFCLASS64      2 // invalid in this OS (no 64-bit support), here for future expansion

// data encoding (ie, endianness)
#define ELFDATANONE     0
#define ELFDATALSB      1
#define ELFDATAMSB      2

// special section indexes
#define SHN_UNDEF       0
#define SHN_LOPRESERVE  0xff00
#define SHN_LOPROC      0xff00
#define SHN_HIPROC      0xff1f
#define SHN_ABS         0xfff1
#define SHN_COMMON      0xfff2
#define SHN_HIRESERVE   0xffff

// section types
#define SHT_NULL        0
#define SHT_PROGBITS    1
#define SHT_SYMTAB      2
#define SHT_STRTAB      3
#define SHT_RELA        4
#define SHT_HASH        5
#define SHT_DYNAMIC     6
#define SHT_NOTE        7
#define SHT_NOBITS      8
#define SHT_REL         9
#define SHT_SHLIB       10
#define SHT_DYNSYM      11
#define SHT_LOPROC      0x70000000
#define SHT_HIPROC      0x7fffffff
#define SHT_LOUSER      0x80000000
#define SHT_HIUSER      0xffffffff

// section header flags
#define SHF_WRITE       0x1
#define SHF_ALLOC       0x2
#define SHF_EXECINSTR   0x4
#define SHF_MASKPROC    0xf0000000

// symbol table
#define STN_UNDEF       0

// symbol bindings
#define STB_LOCAL       0
#define STB_GLOBAL      1
#define STB_WEAK        2
#define STB_LOPROC      13
#define STB_HIPROC      15

// symbol types
#define STT_NOTYPE      0
#define STT_OBJECT      1
#define STT_FUNC        2
#define STT_SECTION     3
#define STT_FILE        4
#define STT_LOPROC      13
#define STT_HIPROC      15

// relocation types
#define R_386_NONE      0
#define R_386_32        1
#define R_386_PC32      2
#define R_386_GOT32     3
#define R_386_PLT32     4
#define R_386_COPY      5
#define R_386_GLOB_DAT  6
#define R_386_JMP_SLOT  7
#define R_386_RELATIVE  8
#define R_386_GOTOFF    9
#define R_386_GOTPC     10

// program header types
#define PT_NULL         0
#define PT_LOAD         1
#define PT_DYNAMIC      2
#define PT_INTERP       3
#define PT_NOTE         4
#define PT_SHLIB        5
#define PT_PHDR         6
#define PT_LOPROC       0x70000000
#define PT_HIPROC       0x7fffffff

// program header flags
#define PF_X            (1 << 0) // exectuable
#define PF_W            (1 << 1) // writable
#define PF_R            (1 << 2) // readable

// dynamic section tags
/*                            d_un    exectuable  shared object */
#define DT_NULL         0 //  ignored mandatory   mandatory
#define DT_NEEDED       1 //  d_val   optional    optional
#define DT_PLTRELSZ     2 //  d_val   optional    optional
#define DT_PLTGOT       3 //  d_ptr   optional    optional
#define DT_HASH         4 //  d_ptr   mandatory   mandatory
#define DT_STRTAB       5 //  d_ptr   mandatory   mandatory
#define DT_SYMTAB       6 //  d_ptr   mandatory   mandatory
#define DT_RELA         7 //  d_ptr   mandatory   optional
#define DT_RELASZ       8 //  d_val   mandatory   optional
#define DT_RELAENT      9 //  d_val   mandatory   optional
#define DT_STRSZ        10 // d_val   mandatory   mandatory
#define DT_SYMENT       11 // d_val   mandatory   mandatory
#define DT_INIT         12 // d_ptr   optional    optional
#define DT_FINI         13 // d_ptr   optional    optional
#define DT_SONAME       14 // d_val   ignored     optional
#define DT_RPATH        15 // d_val   optional    ignored
#define DT_SYMBOLIC     16 // ignored ignored     optional
#define DT_REL          17 // d_ptr   mandatory   optional
#define DT_RELSZ        18 // d_val   mandatory   optional
#define DT_RELENT       19 // d_val   mandatory   optional
#define DT_PLTREL       20 // d_val   optional    optional
#define DT_DEBUG        21 // d_ptr   optional    ignored
#define DT_TEXTREL      22 // ignored optional    optional
#define DT_JMPREL       23 // d_ptr   optional    optional
#define DT_LOPROC       0x70000000
#define DT_HIPROC       0x7fffffff

// macros for individual file sections and performing actions with them
#define STRING(image, offset) ((char *)(&((image)->ei_strtab[(offset)])))
#define SYMNAME(image, sym) STRING(image, (sym)->st_name)
#define SYMBOL(image, num) ((struct Elf32_Sym *)&(image)->ei_symtab[num])
#define HASHTABSIZE(image) ((image)->ei_symhash[0])
#define HASHBUCKETS(image) ((unsigned int *)&(image)->ei_symhash[2])
#define HASHCHAINS(image) ((unsigned int *)&(image)->ei_symhash[2+HASHTABSIZE(image)])

// macros for relocation tables
#define ELF32_R_SYM(i)		((i)>>8)
#define ELF32_R_TYPE(i)		((unsigned char)(i))
#define ELF32_R_INFO(s,t)	(((s)<<8)+(unsigned char)(t))

// macros for symbols
#define ELF32_ST_BIND(i)	((i)>>4)
#define ELF32_ST_TYPE(i)	((i)&0xf)
#define ELF32_ST_INFO(b,t)	(((b)<<4)+((t)&0xf))

// Prototypes

// loads an elf binary into the current address space
int32_t elf_load( const char* filename, uint32_t* entry_point );

// loads a static binary from a file
//int32_t LoadStaticBinaryFromFile( int fd, uint32_t kerncr3, uint32_t taskflags, uint32_t newcr3_override, char** argv );

// loads a static binary from a file
int32_t LoadStaticBinaryFromFile( const char* exename, uint32_t kerncr3, uint32_t taskflags, uint32_t newcr3_override, char** argv );

// loads a kernel module
int32_t LoadKernelModule( uint32_t kcr3, uint32_t filebase, const char* modnm );

// loads an ELF binary (type: 1 = kmod, 0 = static) - a wrapper for the above two functions
int32_t LoadElf( uint32_t kcr3, uint32_t base, uint32_t type, uint32_t taskflags, uint32_t newcr3_override, const char* modnm, char** argv );

// loads a dynamic elf binary
//void LoadELFDyn( uint32_t kerncr3, uint32_t elfptr, uint32_t taskflags, multiboot_info_t* mboot_ptr );

// loads the kernel image for use in resolving symbols
void InitElfSystems( multiboot_info_t* mboot_ptr );

// loads the kernel symbol table ready for use - also makes sure it cannot
// be modified?
void InitKernelSymbolTable( multiboot_info_t* mboot_ptr );

// returns a symbol from a specific address within the kernel
// although, it doesn't actually return anything O_O
char* getkernsym( uint32_t addr );

#endif
